/**
 * @file httpServer.cpp
 * @author your name (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2024-07-20
 *
 * @copyright Copyright (c) 2024
 *
 */

#include "httpSvr/httpServer.h"

#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

#include <cstring>
#include <thread>

#include "basic/trace.h"
#include "httpHandler.h"

namespace wheels {

HttpServer::HttpServer(std::shared_ptr<IRouter> &r) : 
    m_clientHandler(std::make_unique<HttpHandler>(r)), m_router(r) {}

HttpServer::~HttpServer() { closeSock(); }

int HttpServer::Start(const char *address, uint16_t port) {
    closeSock();
    auto sock = createListenSock(address, port);
    if (sock == -1) {
        trace_err("socket fail:%s\n", strerror(errno));
        return -1;
    }

    m_sockFd = sock;
    m_looping = true;
    trace_info("start http server:%s:%d\n", address, port);
    std::thread t(&HttpServer::OnAccept, this, sock);
    m_thread = std::move(t);
    return 0;
}

int HttpServer::createListenSock(const char *address, uint16_t port) {
    auto sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
        trace_err("socket fail:%s\n", strerror(errno));
        return -1;
    }

    do {
        int opt = 1;
        if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) != 0) {
            trace_err("setsockopt fail:%s\n", strerror(errno));
            return -1;
        }

        struct sockaddr_in addr;
        memset(&addr, 0x0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(address);
        if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
            trace_err("bind fail:%s\n", strerror(errno));
            break;
        }

        listen(sock, 10);
        return sock;
    } while (0);

    close(sock);
    return -1;
}

void HttpServer::OnAccept(int sock) {
    while (m_looping) {
        struct sockaddr_in addr;
        socklen_t sockLen = sizeof(addr);
        int client = accept(sock, (struct sockaddr *)&addr, &sockLen);
        if (client == -1) {
            trace_err("accept fail:[%d]%s\n", errno,strerror(errno));
            break;
        }

        trace_info("accept client:%s\n", inet_ntoa(addr.sin_addr));
        m_clientHandler->OnHandleClient(client, inet_ntoa(addr.sin_addr));
    }
}

int HttpServer::Stop(void) {
    m_looping = false;
    shutdown(m_sockFd, SHUT_RD);
    closeSock();
    m_thread.join();
    return 0;
}

}  // namespace wheels
